package 网络编程项目;

import 网络编程项目.login.utiles.LoginUtil;
import 网络编程项目.login.utiles.ServerToReceiveFile;
import 网络编程项目.pojo.Message;
import 网络编程项目.pojo.Response;
import 网络编程项目.pojo.ResponseType;
import 网络编程项目.util.ObjectUtils;
import 网络编程项目.util.PathUtils;

import java.io.*;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.*;

/**
 * 需要任何成员变量都可以加，实体类的属性也自己加
 */
public class Server {

    private static final int PORT = 9100;

    private static final int DEFAULT_BUFFER_SIZE = 1024;

    //文件上传目录
    private static final String baseDir = "D:\\upload";

    private ServerSocketChannel serverSocketChannel;

    private Selector selector;

    private ListenerThread listenerThread;

    private ExecutorService pool;


    public Server() throws IOException {
        init();
    }

    private void init() throws IOException {

        serverSocketChannel = ServerSocketChannel.open();

        serverSocketChannel.bind(new InetSocketAddress(PORT));

        serverSocketChannel.configureBlocking(false);

        selector = Selector.open();

        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

        listenerThread = new ListenerThread();

        pool = new ThreadPoolExecutor(6,8,60L, TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(20),(task,excutor)-> {
            try {
                excutor.getQueue().offer(task,1,TimeUnit.MINUTES);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
    }

    public void start(){

        listenerThread.start();

    }


    public void shutdown(){

    }

    private void handleRequest() throws IOException {
        SocketChannel socketChannel = serverSocketChannel.accept();
        socketChannel.configureBlocking(false);
        socketChannel.register(selector,SelectionKey.OP_READ);
    }


    /**
     * 监听所有客户端发来的消息，在Task类里面添加处理Message的方法
     */
    private class ListenerThread extends Thread{

        @Override
        public void run() {

            Thread currentThread = Thread.currentThread();

            try {

                while (!currentThread.isInterrupted()){

                    //select()方法会阻塞等待有可处理事件发生
                    if (selector.select()>0){

                        Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();

                        while (iterator.hasNext()){
                            SelectionKey key = iterator.next();

                            iterator.remove();

                            //连接事件就绪
                            if (key.isAcceptable()){

                                handleRequest();

                                //读事件就绪
                            }else if (key.isReadable()){

                                //关闭可读触发标记
                                key.interestOps(key.interestOps() & (~SelectionKey.OP_READ));

                                //交给线程池处理任务
                                pool.execute(new Task(key));
                            }
                        }
                    }

                }
            } catch (IOException e){
                e.printStackTrace();
            }

        }
    }

    /**
     * 添加处理Message的方法......
     */
    private class Task implements Runnable{

        private SelectionKey key;

        private ByteBuffer buffer;

        private ByteArrayOutputStream bos;

        private Message message;

        public Task(SelectionKey key){
            this.key = key;
            buffer = ByteBuffer.allocate(DEFAULT_BUFFER_SIZE);
            bos = new ByteArrayOutputStream();
        }


        @Override
        public void run() {

           SocketChannel channel = (SocketChannel) key.channel();

            try {
                //读取信息
                int len = 0;
                while ((len = channel.read(buffer))>0){
                    buffer.flip();
                    bos.write(buffer.array(),0,len);
                    buffer.clear();
                }
                //读取完毕，打开可读触发标记
                key.interestOps(key.interestOps() | SelectionKey.OP_READ);
                //主动唤醒selector
                selector.wakeup();

                //反序列化出Message对象
                byte[] bytes = bos.toByteArray();
                message = ObjectUtils.deserialize(bytes);
//                System.out.println("收到消息："+message);


                //TODO 根据消息的不同类型进行不同的处理
                switch (message.getType()){

                    // 举个例子:
                    case EXAMPLE:handleExample();break;

                    case DOWNLOAD:handleDownload();break;

                    case REQUEST_FILENAMES:handleRequestFiles();break;

                    case REQUEST_ONLINE_USERS:handleRequestOnlineUsers();break;

                    case UPLOAD:handleUpload();break;

                    case BROADCAST:handleBroadCast();break;

                    case TO_ONE:handleToOne();break;

                    case LOGIN:
                        LoginUtil.login(key, message);
                        broadCastOnlineUsers();
                        break;
                    case REGISTER:
                        handleRegister();
                        break;

                }


            } catch (IOException e) {

                key.cancel();
                //进行下线处理
                Map<String, SelectionKey> onlineUser = LoginUtil.OnlineUser;

                    for (String uname : onlineUser.keySet()) {
                        if (onlineUser.get(uname)==key){
                            onlineUser.remove(uname);
                            System.out.println("用户["+uname+"]已下线");
                            break;
                        }
                    }

                broadCastOnlineUsers();

                System.out.println(onlineUser);

            }catch (ClassNotFoundException e){
                e.printStackTrace();
            }
        }




        /**
         * 举例说明
         */
        private void handleExample() throws IOException {
            //拿到在Selector中注册的channel的key的集合
            Set<SelectionKey> keys = selector.keys();
            //遍历这个集合
            Iterator<SelectionKey> iterator = keys.iterator();

            Response response = new Response();
            response.setType(ResponseType.EXAMPLE);
            response.setMsg(message.getMsg());

            //转发给所有客户端
            while (iterator.hasNext()){
                SelectionKey key = iterator.next();

                //这里排除掉 ServerSocketChannel 自己
                if (key.channel() instanceof SocketChannel) {
                    SocketChannel channel = (SocketChannel) key.channel();
                    channel.write(ByteBuffer.wrap(ObjectUtils.serialize(response)));
                }
            }
        }

        private void handleBroadCast() throws IOException {

            Response response = new Response();
            response.setType(ResponseType.BROADCAST);
            response.setMsg(message.getMsg());
            response.setSender(message.getSender());

            handleBroadCast(response);
        }

        private void handleToOne(){

            Response response = new Response();
            response.setSender(message.getSender());
            response.setMsg(message.getMsg());
            response.setType(ResponseType.TO_ONE);

            SelectionKey key = LoginUtil.OnlineUser.get(message.getReceiver());
            SocketChannel channel = (SocketChannel)key.channel();
            try {
                channel.write(ByteBuffer.wrap(ObjectUtils.serialize(response)));
            } catch (IOException e) {
                e.printStackTrace();
                System.out.println("私聊信息发送失败");
            }

        }

        private void handleBroadCast(Response response) throws IOException {

            //拿到在Selector中注册的channel的key的集合
            Set<SelectionKey> keys = selector.keys();
            //遍历这个集合
            Iterator<SelectionKey> iterator = keys.iterator();

            //转发给所有客户端
            while (iterator.hasNext()){
                SelectionKey key = iterator.next();

                //这里排除掉 ServerSocketChannel 自己
                if (key.channel() instanceof SocketChannel) {
                    SocketChannel channel = (SocketChannel) key.channel();
                    try {
                        channel.write(ByteBuffer.wrap(ObjectUtils.serialize(response)));
                    } catch (IOException e) {
                        System.out.println("该连接已关闭"+channel.toString());
                    }
                }
            }

        }

        private void handleUpload() throws IOException {

            //获取文件内容
            byte[] fileContent = message.getFileContent();

            //获取文件名称
            String fileName = message.getFileName();
            System.out.println("即将上传文件: "+fileName);
            //创建目录
            File file = new File(baseDir);
            if (!file.exists()) file.mkdirs();

            //完成文件上传
            FileOutputStream fos = new FileOutputStream(new File(baseDir,fileName));
            fos.write(fileContent);

            fos.close();
        }

        private void handleRequestFiles() throws IOException {

            File file = new File(baseDir);

            if (!file.exists()) file.mkdirs();

            String[] filenames = file.list();

            Response response = new Response();
            response.setType(ResponseType.RESPONSE_FILENAMES);
            response.setFilenames(filenames);

            SocketChannel channel = (SocketChannel) key.channel();
            channel.write(ByteBuffer.wrap(ObjectUtils.serialize(response)));

        }

        private void handleRequestOnlineUsers() throws IOException {
            Map<String, SelectionKey> onlineUser = LoginUtil.OnlineUser;
            Response response = new Response();
            response.setType(ResponseType.RESPONSE_ONLINE_USERS);
            response.setOnlineUsers(onlineUser.keySet().toArray());
            SocketChannel channel = (SocketChannel) key.channel();
            channel.write(ByteBuffer.wrap(ObjectUtils.serialize(response)));
        }

        private void broadCastOnlineUsers() {

            Map<String, SelectionKey> onlineUser = LoginUtil.OnlineUser;
            Response response = new Response();
            response.setType(ResponseType.RESPONSE_ONLINE_USERS);
            response.setOnlineUsers(onlineUser.keySet().toArray());

            try {
                handleBroadCast(response);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        private void handleDownload() throws IOException {

            String source = message.getFileName();

            File file = new File(baseDir);

            if (!file.exists()) file.mkdirs();

            FileInputStream fis = new FileInputStream(new File(baseDir,source));

            FileChannel inChannel = fis.getChannel();
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();

            int length = 0;
            buffer.clear();
            while ((length = inChannel.read(buffer)) != -1) {
                buffer.flip();
                byteArrayOutputStream.write(buffer.array(), 0, length);
                buffer.clear();
            }

            Response response = new Response();
            response.setType(ResponseType.DOWNLOAD);
            response.setFileContent(byteArrayOutputStream.toByteArray());
            response.setFileName(source);

            SocketChannel channel = (SocketChannel) key.channel();
            channel.write(ByteBuffer.wrap(ObjectUtils.serialize(response)));

            fis.close();
            inChannel.close();

        }
        private void handleRegister() throws IOException {
            if (LoginUtil.findID(message)) {
                //UserID已经存在
                Response userID_is_exist = new Response();
                userID_is_exist.setType(ResponseType.USERID_IS_EXIST);

                LoginUtil.sendResponse(key, userID_is_exist);
            } else {
                //发送允许发送的消息
                boolean bool = LoginUtil.register(message);
                if (bool) {
                    //注册成功
                    Response msg_register_success = new Response();
                    msg_register_success.setType(ResponseType.USER_RESISTER_SUCCESS);
                    LoginUtil.sendResponse(key, msg_register_success);
                } else {
                    //注册失败
                    Response msg_register_fail = new Response();
                    msg_register_fail.setType(ResponseType.USER_REGISTER_FAIL);
                    LoginUtil.sendResponse(key, msg_register_fail);
                }
            }
        }

    }

    public static void main(String[] args) throws IOException {
        new Thread(() -> {
            try {
                new ServerToReceiveFile().startServer();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }).start();
        new Server().start();
    }


}
